package com.googlecode.lookie.platform.flexdock;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.AbstractButton;
import javax.swing.JComponent;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockableFactory;
import org.flexdock.docking.DockingManager;
import org.flexdock.docking.state.PersistenceException;
import org.flexdock.perspective.PerspectiveManager;
import org.flexdock.view.View;
import org.flexdock.view.ViewProps;
import org.flexdock.view.Viewport;
import org.springframework.richclient.application.PageComponent;
import org.springframework.richclient.application.ViewDescriptor;
import org.springframework.richclient.application.support.AbstractApplicationPage;

public class FlexDockApplicationPage
    extends AbstractApplicationPage
    implements DockableFactory
{

    private PropertyChangeListener activeHandler = new PropertyChangeListener()
    {
        public void propertyChange( PropertyChangeEvent evt )
        {
            if ( ViewProps.ACTIVE.equals( evt.getPropertyName() ) && Boolean.TRUE.equals( evt.getNewValue() ) )
            {
                View view = (View) evt.getSource();
                PageComponent component = findPageComponent( view.getPersistentId() );
                setActiveComponent( component );
            }
        }
    };

    Viewport viewport;

    private Map<String, Dockable> dockables = new HashMap<String, Dockable>();

    private boolean creatingDockable;

    private boolean isLoadingLayout;

    protected View createView( final PageComponent component )
    {
        View view = new View( component.getId() );
        view.setTitle( component.getDisplayName() );
        view.setTabText( component.getDisplayName() );
        view.setTabIcon( component.getIcon() );
        view.setIcon( component.getIcon() );
        view.setContentPane( component.getControl() );

        view.getViewProperties().addPropertyChangeListener( activeHandler );

        configureView( component, view, getViewDescriptor( component.getId() ) );

        dockables.put( component.getId(), view );

        return view;
    }

    protected void configureView( final PageComponent component, View view, ViewDescriptor descriptor )
    {
        boolean closable = true;
        boolean pinnable = true;
        boolean dockable = true;

        if ( descriptor instanceof FlexDockViewDescriptor )
        {
            FlexDockViewDescriptor desc = (FlexDockViewDescriptor) descriptor;
            closable = desc.isClosable();
            pinnable = desc.isPinnable();
            dockable = desc.isDockable();
        }

        if ( closable )
        {
            view.addAction( View.CLOSE_ACTION );
            // TODO fix this: this is the only way I found to find out if a dockable has
            // been closed by the user.
            AbstractButton btn = view.getActionButton( View.CLOSE_ACTION );
            btn.addActionListener( new ActionListener()
            {
                public void actionPerformed( ActionEvent e )
                {
                    close( component );
                }
            } );
        }

        if ( pinnable )
        {
            view.addAction( View.PIN_ACTION );
        }

        view.getViewProperties().setDockingEnabled( dockable );
    }

    public View getView( String id )
    {
        return (View) dockables.get( id );
    }

    public void loadLayout()
    {
        isLoadingLayout = true;

        // the view port must be created before we attempt to load the layout model or try
        // to restore the layout
        getControl();

        try
        {
            DockingManager.loadLayoutModel( false );
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        catch ( PersistenceException e )
        {
            e.printStackTrace();
        }

        viewport.revalidate();
        viewport.repaint();

//        PerspectiveManager.getInstance().setCurrentPerspective( "default" );
        DockingManager.restoreLayout();

        isLoadingLayout = false;

        // mark the view associated with the active component as active
        if ( getActiveComponent() != null )
        {
            View view = getView( getActiveComponent().getId() );
            if ( view != null )
            {
                view.setActive( true );
            }
        }
    }

    protected void doAddPageComponent( PageComponent pageComponent )
    {
        View view = createView( pageComponent );
        dockables.put( pageComponent.getId(), view );

        if ( !isLoadingLayout )
        {
            DockingManager.display( view );
        }
    }

    @Override
    protected ViewDescriptor getViewDescriptor( String viewDescriptorId )
    {
        ViewDescriptor descriptor = super.getViewDescriptor( viewDescriptorId );
        return descriptor;
    }

    protected void doRemovePageComponent( PageComponent pageComponent )
    {
        View view = getView( pageComponent.getId() );
        if ( view != null )
        {
            DockingManager.close( (Dockable) view );

            // HACK: if we don't repaint here, when closing the last dockable the ui is
            // not updated
            viewport.revalidate();
            viewport.repaint();

            dockables.remove( view );
        }
    }

    protected boolean giveFocusTo( PageComponent pageComponent )
    {
        if ( creatingDockable )
        {
            return false;
        }

        View view = getView( pageComponent.getId() );

        view.setActive( true );

        // HACK: otherwise the first dockable that was active will still be active
        for ( Object o : DockingManager.getDockableIds() )
        {
            String id = o.toString();
            if ( !id.equals( pageComponent.getId() ) )
            {
                getView( id ).setActive( false );
            }
        }

        return true;
    }

    protected JComponent createControl()
    {
        viewport = new Viewport();
        return viewport;
    }

    public Dockable getDockable( String id )
    {
        if ( dockables.containsKey( id ) )
        {
            return (Dockable) dockables.get( id );
        }

        creatingDockable = true;
        showView( id );
        creatingDockable = false;

        return (Dockable) dockables.get( id );
    }

    public Component getDockableComponent( String dockableId )
    {
        return null;
    }

}
